--[[
    用来根据在线玩家数量刷新红方战机的脚本
]]
jetSpawner = {

-------------------------------------
--          配置参数区            --
-------------------------------------
    version = '0.0.3',

    DebugMode = false, -- 调试模式开关

    -- 定义预设红方飞机群组
    jetPresets = {
        { 
            categoryName = '1', -- 飞机类型预设
            groups = {'1-2','2-2','3-2','4-2','5-2','6-2','7-2','8-2','9-2','10-2','11-2','12-2','13-2','14-2','15-2','16-2'}, -- 预设的群组名, 每次刷新随机抽取
            baseTime = 909, -- 基础刷新时间
            maxSpawn = 1, -- 最大数量限制, 设置为 >0 的数视为启用, 设置为 <=0 的数或者 nil/false 视为不启用.
        },
        { 
            categoryName = '2', 
            groups = {'1-3','2-3','3-3','4-3','5-3','6-3','7-3','8-3','9-3','10-3','11-3','12-3','13-3','14-3','15-3','16-3'}, 
            baseTime = 1824, 
            maxSpawn = 1, 
        },
        { 
            categoryName = '3', 
            groups = {'1-4','2-4','3-4','4-4','5-4','6-4','7-4','8-4','9-4','10-4','11-4','12-4','13-4','14-4','15-4','16-4'}, 
            baseTime = 1430, 
            maxSpawn = 1, 
        },
        { 
            categoryName = '4', 
            groups = {'1-5','2-5','3-5','4-5','5-5','6-5','7-5','8-5','9-5','10-5','11-5','12-5','13-5','14-5','15-5','16-5'}, 
            baseTime = 1879, 
            maxSpawn = 2, 
        },
        { 
            categoryName = '5', 
            groups = {'1-6','2-6','3-6','4-6','5-6','6-6','7-6','8-6','9-6','10-6','11-6','12-6','13-6','14-6','15-6','16-6'}, 
            baseTime = 2346, 
            maxSpawn = 1, 
        },
        { 
            categoryName = '6', 
            groups = {'1-7','2-7','3-7','4-7','5-7','6-7','7-7','8-7','9-7','10-7','11-7','12-7','13-7','14-7','15-7','16-7'}, 
            baseTime = 2878, 
            maxSpawn = 2, 
        },
        { 
            categoryName = '7', 
            groups = {'1-8','2-8','3-8','4-8','5-8','6-8','7-8','8-8','9-8','10-8','11-8','12-8','13-8','14-8','15-8','16-8'}, 
            baseTime = 2010,  
            maxSpawn = 1,     
        },
    },

    -- 根据玩家调整刷新率
    player_ratio_table = {
        [3] = 4.5,      -- 当玩家数小于3人刷新率为 3
        [5] = 2.5,
        [10] = 1.5,  
        [20] = 1,
        [30] = 0.75,
        [40] = 0.55,
        [50] = 0.45,
        [60] = 0.35,
        [9999] = 0.25,
    },

    -- 是否仅计算已升空玩家
    only_count_airborne_players = true,

    RANDOM_DEVIATION = 0.5,  -- 时间随机浮动比例（±50%）
-------------------------------------
--          配置参数区 END         --
-------------------------------------

    jetTemplate = {},
    playerList = {},
    allMonitors = {},
    spawnedGroups = {},
}

do
    --Tools
    function deepCopy(object)
		local lookup_table = {}
		local function _copy(object)
			if type(object) ~= "table" then
				return object
			elseif lookup_table[object] then
				return lookup_table[object]
			end
			local new_table = {}
			lookup_table[object] = new_table
			for index, value in pairs(object) do
				new_table[_copy(index)] = _copy(value)
			end
			return setmetatable(new_table, getmetatable(object))
		end
		return _copy(object)
	end

    function Debug(msg,time)
        if jetSpawner.DebugMode then
            local time = time or 10
            trigger.action.outText("[DEBUG] "..msg,time)
        end
    end

    -- 获取红方机预设
    local red_jets = env.mission.coalition.red.country[1].plane.group
    for i, presetData in ipairs(jetSpawner.jetPresets) do
        jetSpawner.jetTemplate[presetData.categoryName] = jetSpawner.jetTemplate[presetData.categoryName] or {}
        for j, groupName in ipairs(presetData.groups) do
            for k, groupData in ipairs(red_jets) do
                if groupData.name == groupName then
                    local tempData = deepCopy(groupData)
                    tempData.lateActivation = false
                    -- 直接以群组名为键存储
                    jetSpawner.jetTemplate[presetData.categoryName][groupName] = tempData
                end
            end
        end
    end

    -- 更新玩家数量
    function jetSpawner:addplayerList(playerName,event)
        if not self.playerList[playerName] then
            self.playerList[playerName] = true
        end

        --Debug
        if not event then return end
        local eventTable = {
            [world.event.S_EVENT_BIRTH] = 'S_EVENT_BIRTH',
            [world.event.S_EVENT_TAKEOFF] = 'S_EVENT_TAKEOFF',
        }
        Debug("Player number +1 by \'event "..eventTable[event.id].." handler.\' Player name: "..playerName..' current playerNum: '..jetSpawner.tableCount(self.playerList))
    end

    function jetSpawner:deleteplayerList(playerName,event)
        if self.playerList[playerName] then
            self.playerList[playerName] = nil
        end

        --Debug
        if not event then return end
        local eventTable = {
            [world.event.S_EVENT_CRASH] = 'S_EVENT_CRASH',
            [world.event.S_EVENT_PLAYER_LEAVE_UNIT] = 'S_EVENT_PLAYER_LEAVE_UNIT',
        }
        Debug("Player number -1 by \'event "..eventTable[event.id].." handler.\' Player name: "..playerName..' current playerNum: '..jetSpawner.tableCount(self.playerList))
    end

    function jetSpawner.tableCount(table)
        if not table then return end
        if type(table) ~= 'table' then return end

        local count = 0
        for _ in pairs(table) do
            count = count + 1
        end
        return count
    end

    local eventHandler = {}
    function eventHandler:onEvent(event)
        --- playerNum +1
        -- 仅计算起飞玩家: 关
        if not jetSpawner.only_count_airborne_players then
            if event.id == world.event.S_EVENT_BIRTH then
                local unit = event.initiator
                if not unit or not unit.getPlayerName then return end

                local playerName = unit:getPlayerName()
                if playerName then
                    jetSpawner:addplayerList(playerName,event)
                end
            end
        end

        -- 仅计算起飞玩家: 开
        if jetSpawner.only_count_airborne_players then
            if event.id == world.event.S_EVENT_TAKEOFF then
                local unit = event.initiator
                if not unit or not unit.getPlayerName then return end

                local playerName = unit:getPlayerName()
                if playerName then
                    jetSpawner:addplayerList(playerName,event)
                end
            end
        end

        -- playerNum -1
        if event.id == world.event.S_EVENT_CRASH or event.id == world.event.S_EVENT_PLAYER_LEAVE_UNIT then
            local unit = event.initiator
            if not unit or not unit.getPlayerName then return end

            local playerName = unit:getPlayerName()
            if playerName then
                jetSpawner:deleteplayerList(playerName,event)
            end
        end
    end
    world.addEventHandler(eventHandler)

    -- 为每一种预设飞机添加刷新控制器
    function jetSpawner._spawnMonitor(vars,time)
        local self = vars.context
        local preset = vars.preset
        --抽取要刷新的飞机
        local spawnGroupName = preset.groups[math.random(1,#preset.groups)]
        Debug('spawnGroupName: '..spawnGroupName..' categoryName: '..preset.categoryName)

        local spawnGroupData = self.jetTemplate[preset.categoryName][spawnGroupName]
        if not spawnGroupData then
            Debug('Can not get spawnGroupData')
            env.error('jetSpawner: Can not get spawnGroupData for group: '..spawnGroupName..', please check \"jetPresets\"')
            return time + 100
        end
        -- 给新群组命名
        spawnGroupData.name = nil
        local newGroupName = preset.categoryName..'_'..spawnGroupData.units[1].type

        if self.spawnedGroups[preset.categoryName] then
            if self.spawnedGroups[preset.categoryName][newGroupName] then
                for groupName,group in pairs(self.spawnedGroups[preset.categoryName][newGroupName]) do
                    if not group:isExist() then
                        spawnGroupData.name = groupName
                        self.spawnedGroups[preset.categoryName][newGroupName][groupName] = nil
                        break
                    end
                end
            end
        end

        if not spawnGroupData.name then
            -- 获取群组序号
            local categoryTable = self.spawnedGroups[preset.categoryName] or {}
            local tempTable = categoryTable[newGroupName] or {}
            local groupNameIndex = jetSpawner.tableCount(tempTable) or 0
            spawnGroupData.name = newGroupName..'-'..(groupNameIndex + 1)
        end
        for i,unitData in ipairs(spawnGroupData.units) do
            unitData.name = spawnGroupData.name..'-'..i
        end

        -- 判断是否到达最大数量限制
        local canSpawn = true
        if preset.maxSpawn and preset.maxSpawn > 0 then
            local count = 0
            if self.spawnedGroups[preset.categoryName] then
                for groupName, group in pairs(self.spawnedGroups[preset.categoryName]) do
                    count = count + jetSpawner.tableCount(self.spawnedGroups[preset.categoryName][groupName])
                end
            end
            if count >= preset.maxSpawn then
                canSpawn = false
            end
            Debug('Category: '..preset.categoryName..' have spawned: ('..count..'/'..preset.maxSpawn..')')
        end

        -- 刷新飞机
        if canSpawn then
            local presetGroup = Group.getByName(spawnGroupName)
            if presetGroup then
                local category = presetGroup:getCategory()
                local newGroup = coalition.addGroup(country.id.CJTF_RED,category,spawnGroupData)
                if newGroup then
                    self.spawnedGroups[preset.categoryName] = self.spawnedGroups[preset.categoryName] or {}
                    self.spawnedGroups[preset.categoryName][newGroupName] = self.spawnedGroups[preset.categoryName][newGroupName] or {}
                    self.spawnedGroups[preset.categoryName][newGroupName][spawnGroupData.name] = newGroup
                end
            end
        end

        --设定下次刷新时间
        local Ratio = 0.05
        for maxPlayer,ratio in pairs(self.player_ratio_table) do
            if self.tableCount(self.playerList) <= maxPlayer then
                Ratio = ratio
                break
            end
        end

        local nextSpawnTime = preset.baseTime
        local minTime = math.max(0, nextSpawnTime * (1 - self.RANDOM_DEVIATION))
        local maxTime = nextSpawnTime * (1 + self.RANDOM_DEVIATION)
        nextSpawnTime = math.random(minTime, maxTime)

        Debug('playerNumber:' ..self.tableCount(self.playerList)..' ratio: '..Ratio..' Preset:'..preset.categoryName..' nextSpawnTime: '..nextSpawnTime * Ratio)

        return time + nextSpawnTime * Ratio
    end

    function jetSpawner:_spawnMonitorStart(preset,delay)
        if not preset then return end

        local delay = delay or preset.baseTime or 1
        if delay <= 0 then
            delay = preset.baseTime or 1
        end
        local timerID = timer.scheduleFunction(self._spawnMonitor,{context = self, preset = preset},timer.getTime()+delay)

        return timerID
    end

    function jetSpawner:_spawnMonitorStop(presetName)
        local timerID = jetSpawner.allMonitors[presetName]
        if not timerID then return end

        timer.removeFunction(timerID)
        jetSpawner.allMonitors[presetName] = nil
    end

    function jetSpawner:startAllMonitors()
        for i, presetData in ipairs(self.jetPresets) do
            local preset = presetData -- 局部变量避免闭包问题
            self.allMonitors[preset.categoryName] = self:_spawnMonitorStart(preset)
        end
    end


    jetSpawner:startAllMonitors()
    Debug('jetSpawner Ver: '..jetSpawner.version)
end



















-- Author: Buta
-- Date: 2025-03-02